// 071114;080305 alexch
//
// ==========  This file is under  LGPL, the GNU Lesser General Public Licence
// ==========  Dialing Lemmatizer (www.aot.ru)
// ==========  Copyright by Alexey Sokirko 
//
// ==========  Visual Studio project for 32/64 bit DLL-wrapper
// ==========  Created by Hulbee AG

#pragma warning(disable: 4786)
#include "StlSysF.h"

#include <wchar.h>
#include <limits.h>
#include <stdarg.h>
#include <assert.h>

#include <algorithm>

namespace StlFunc {

uint MaxStackAlloc = 65536;

// {
template <class TYPE> inline static TYPE& TSetStr(TYPE &dst,
	const typename TYPE::value_type *src, int maxLen)
{
	dst.erase();
	if (maxLen <= 0)
		return dst;
	int i;
	for (i = 0; i < maxLen && src[i]; ++i)
		;
	return dst.assign(src, i);
}

#define SET_STR(TYPE) TYPE& SetStr(TYPE &dst, const TYPE::value_type *src, int maxLen) \
{ \
	return TSetStr<TYPE>(dst, src, maxLen); \
}

SET_STR(str_t)
SET_STR(wstr_t)
// }

str_t Str(const char *src, int maxLen)
{
	str_t s;
	return SetStr(s, src, maxLen);
}

wstr_t Str(const wchar_t *src, int maxLen)
{
	wstr_t s;
	return SetStr(s, src, maxLen);
}

// {
template <class TYPE> inline static bool TGetStr(typename TYPE::value_type *dst,
	int maxLen, const TYPE &src)
{
	if (maxLen <= 0)
		return false;
	bool ret = true;
	size_t len = src.size();
	if (len >= (size_t)maxLen) {
		len = maxLen - 1;
		ret = false;
	}
#ifdef _SECURE_SCL
	std::copy(src.begin(), src.begin() + len,
		stdext::checked_array_iterator<TYPE::value_type*>(dst, maxLen));
#else
	std::copy(src.begin(), src.begin() + len, dst);
#endif
	dst[len] = 0;
	return ret;
}

#define GET_STR(TYPE) bool GetStr(TYPE::value_type *dst, int maxLen, const TYPE &src) \
{ \
	return TGetStr<TYPE>(dst, maxLen, src); \
}

GET_STR(str_t)
GET_STR(wstr_t)
// }

wstr_t& Str2W(const str_t &src, wstr_t &dst)
{
	size_t sz = src.size();
	dst.resize(sz);
	for (size_t i = 0; i < sz; ++i)
		dst[i] = src[i];
	return dst;
}

wstr_t Str2W(const str_t &src)
{
	wstr_t ret;
	return Str2W(src, ret);
}

str_t& W2Str(const wstr_t &src, str_t &dst, char defChar)
{
	size_t sz = src.size();
	dst.resize(sz);
	for (size_t i = 0; i < sz; ++i)
		dst[i] = src[i] < 0x100 ? (char)src[i] : defChar;
	return dst;
}

str_t W2Str(const wstr_t &src, char defChar)
{
	str_t ret;
	return W2Str(src, ret, defChar);
}

bool W2Utf8(wchar_t w, char *dest, const char *limit)
{
	if (w <= 0x7f)
		*dest = (char)w;
	else {
		unsigned char w_hi = w >> 8;
		unsigned char w_lo = (unsigned char)w;
		if (w <= 0x7ff) {
			*dest++ = char(0xc0 | (w_hi << 2) | (w_lo >> 6));
			if (dest >= limit)
				return false;
		}
		else {
			*dest++ = char(0xe0 | (w_hi >> 4));
			if (dest >= limit)
				return false;
			*dest++ = char(0x80 | ((w_hi & 0xf) << 2) | (w_lo >> 6));
			if (dest >= limit)
				return false;
		}
		*dest = char(0x80 | (w_lo & 0x3f));
	}
	return true;
}

str_t& W2Utf8(str_t &dst, const wchar_t *src, size_t len)
{
	dst.erase();
	if (len == 0)
		return dst;
	size_t reserved = 0, dst_size = 0;
	do {
		if (reserved < 3)
			dst.reserve(dst.capacity() + __max(len, 3));
		if (*src <= 0x7f) {
			dst += (char)*src;
			++dst_size;
			--reserved;
		}
		else {
			unsigned char w_hi = *src >> 8;
			unsigned char w_lo = (char)*src;
			char buf[3], *dest = buf;
			if (*src <= 0x7ff)
				*dest++ = char(0xc0 | (w_hi << 2) | (w_lo >> 6));
			else {
				*dest++ = char(0xe0 | (w_hi >> 4));
				*dest++ = char(0x80 | ((w_hi & 0xf) << 2) | (w_lo >> 6));
			}
			*dest++ = char(0x80 | (w_lo & 0x3f));
			ptrdiff_t utf8_size = dest - buf;
			dst.append(buf, utf8_size);
			dst_size += utf8_size;
			reserved -= utf8_size;
		}
		++src;
	} while (--len);
	dst.resize(dst_size);
	return dst;
}

str_t& W2Utf8(str_t &dst, const wstr_t &src)
{
	return W2Utf8(dst, src.data(), src.size());
}

str_t W2Utf8(const wstr_t &src)
{
	str_t s;
	return W2Utf8(s, src);
}

bool AsciiW2Str(const wstr_t &src, str_t &dst)
{
	size_t sz = src.size();
	dst.resize(sz);
	for (size_t i = 0; i < sz; ++i) {
		if (src[i] >= 0x80) {
			dst.erase();
			return false;
		}
		dst[i] = (char)src[i];
	}
	return true;
}

bool AsciiW2Str(const wchar_t *src, str_t &dst)
{
	return AsciiW2Str(wstr_t(src), dst);
}

// {
template<class TYPE> inline static TYPE& TAsciiUpper(TYPE &s)
{
	for (TYPE::iterator it = s.begin(); it != s.end(); ++it)
		if (*it >= 'a' && *it <= 'z')
			*it -= ('a' - 'A');
	return s;
}

#define ASCII_UPPER(TYPE) TYPE& AsciiUpper(TYPE &s) \
{ \
	return TAsciiUpper<TYPE>(s); \
}

ASCII_UPPER(str_t)
ASCII_UPPER(wstr_t)
// }

// {
template<class TYPE> inline static TYPE& TAsciiLower(TYPE &s)
{
	for (TYPE::iterator it = s.begin(); it != s.end(); ++it)
		if (*it >= 'A' && *it <= 'Z')
			*it += ('a' - 'A');
	return s;
}

#define ASCII_LOWER(TYPE) TYPE& AsciiLower(TYPE &s) \
{ \
	return TAsciiLower<TYPE>(s); \
}

ASCII_LOWER(str_t)
ASCII_LOWER(wstr_t)
// }

// {
template<class ELEM_TYPE> inline static bool TIsAscii(const ELEM_TYPE *s)
{
	if (!s)
		return false;
	const ELEM_TYPE *p = s;
	while (*p) {
		if (*p++ >= 0x80)
			return false;
	}
	return true;
}

#define IS_ASCII(ELEM_TYPE) bool IsAscii(const ELEM_TYPE *s) \
{ \
	return TIsAscii<ELEM_TYPE>(s); \
}

IS_ASCII(char)
IS_ASCII(wchar_t)
// }

// {
template<class TYPE> inline static typename TYPE::value_type TLastChar(const TYPE &s)
{
	if (s.empty())
		return 0;
	return *(s.end() - 1);
}

#define LAST_CHAR(TYPE) TYPE::value_type LastChar(const TYPE &s) \
{ \
	return TLastChar<TYPE>(s); \
}

LAST_CHAR(str_t)
LAST_CHAR(wstr_t)
// }

// {
template<class TYPE> inline static TYPE& TEraseLastIf(TYPE &s, typename TYPE::value_type c)
{
	if (!s.empty() && *(s.end() - 1) == c)
		s.erase(s.end() - 1);
	return s;
}

#define ERASE_LAST_IF(TYPE) TYPE& EraseLastIf(TYPE &s, TYPE::value_type c) \
{ \
	return TEraseLastIf<TYPE>(s, c); \
}

ERASE_LAST_IF(str_t);
ERASE_LAST_IF(wstr_t);
// }

bool EndsWith(const str_t &s, const char *tail)
{
	size_t len = strlen(tail);
	if (len > s.size())
		return false;
	return s.compare(s.size() - len, len, tail, len) == 0;
}

bool EndsWith(const wstr_t &s, const wchar_t *tail)
{
	size_t len = wcslen(tail);
	if (len > s.size())
		return false;
	return s.compare(s.size() - len, len, tail, len) == 0;
}

wstr_t& CloseSentence(wstr_t &s)
{
	if (!s.empty()) {
		wchar_t c = *(s.end() - 1);
		if (c < 0x500 && c != '.' && c != '?' && c != '!')
			s += '.';
	}
	return s;
}

#ifndef ONLY_NATIVE

bool StrVPrintf(str_t &dst, const char *fmt, va_list va)
{
	dst.erase();
	char buf[1024], *p;
	size_t nbuf, flen = strlen(fmt);
	if (flen < sizeof(buf)) {
		nbuf = sizeof(buf) - 1;
		p = buf;
	}
	else {
		nbuf = flen;
		p = new char[nbuf + 1];
	}
	int n;
#pragma warning(push)
#pragma warning (disable: 4996)
	while ((n = _vsnprintf(p, nbuf, fmt, va)) < 0) {
#pragma warning(pop)
		if (p != buf)
			delete[] p;
		nbuf += nbuf;
		if (nbuf > MaxPrintfChars)
			return false;
		p = new char[nbuf + 1];
		if (!p)
			return false;
	}
	dst.assign(p, n);
	if (p != buf)
		delete[] p;
	return true;
}

bool StrPrintf(str_t &dst, const char *fmt, ...)
{
	va_list va;
	va_start(va, fmt);
	bool ret = StrVPrintf(dst, fmt, va);
	va_end(va);
	return ret;
}

bool StrVPrintf(wstr_t &dst, const wchar_t *fmt, va_list va)
{
	dst.erase();
	wchar_t buf[1024], *p;
	size_t nbuf, flen = wcslen(fmt);
	if (flen < sizeof(buf) / sizeof(buf[0])) {
		nbuf = sizeof(buf) / sizeof(buf[0]) - 1;
		p = buf;
	}
	else {
		nbuf = flen;
		p = new wchar_t[nbuf + 1];
	}
	int n;
#pragma warning(push)
#pragma warning (disable: 4996)
	while ((n = _vsnwprintf(p, nbuf, fmt, va)) < 0) {
#pragma warning(pop)
		if (p != buf)
			delete[] p;
		nbuf += nbuf;
		if (nbuf > MaxPrintfChars)
			return false;
		p = new wchar_t[nbuf + 1];
		if (!p)
			return false;
	}
	dst.assign(p, n);
	if (p != buf)
		delete[] p;
	return true;
}

bool StrPrintf(wstr_t &dst, const wchar_t *fmt, ...)
{
	va_list va;
	va_start(va, fmt);
	bool ret = StrVPrintf(dst, fmt, va);
	va_end(va);
	return ret;
}

str_t StrPrintf(const char *fmt, ...)
{
	str_t dst;
	va_list va;
	va_start(va, fmt);
	StrVPrintf(dst, fmt, va);
	va_end(va);
	return dst;
}

wstr_t StrPrintf(const wchar_t *fmt, ...)
{
	wstr_t dst;
	va_list va;
	va_start(va, fmt);
	StrVPrintf(dst, fmt, va);
	va_end(va);
	return dst;
}

str_t& FormatHoursMinSec(str_t &stime, uint sec)
{
	stime.erase();
	uint min, hour;
	str_t s;
	if (sec > 60) {
		min = sec / 60;
		sec -= min * 60;
		if (min > 60) {
			hour = min / 60;
			min -= hour * 60;
			StrPrintf(s, "%u hour", hour);
			if (hour > 1)
				s += 's';
			stime = s;
		}
		if (!stime.empty())
			stime += ' ';
		StrPrintf(s, "%u min", min);
		stime += s;
	}
	if (!stime.empty())
		stime += ' ';
	StrPrintf(s, "%u sec", sec);
	stime += s;
	return stime;
}

#endif // ONLY_NATIVE

bool Str2Int(const str_t &src, int &val)
{
	val = 0;
	size_t sz = src.size();
	if (!sz)
		return false;
	const char *p = src.c_str();
	bool neg = *p == '-';
	if (neg || *p == '+') {
		++p;
		if (!--sz)
			return false;
	}
	if (neg) {
		while (*p == '0') {
			++p;
			if (!--sz)
				return true;
		}
		if (strcmp(p, "2147483648") == 0) {
			val = INT_MIN;
			return true;
		}
	}
	for (; sz; --sz, ++p) {
		if (*p >= '0' && *p <= '9') {
			if (val > INT_MAX / 10)
				return false;
			val *= 10;
			int n = *p - '0';
			if (val > INT_MAX - n)
				return false;
			val += n;
		}
		else
			return false;
	}
	if (neg)
		val = -val;
	return true;
}

bool Str2Int(const str_t &src, __int64 &val)
{
	val = 0;
	size_t sz = src.size();
	if (!sz)
		return false;
	const char *p = src.c_str();
	bool neg = *p == '-';
	if (neg || *p == '+') {
		++p;
		if (!--sz)
			return false;
	}
	if (neg) {
		while (*p == '0') {
			++p;
			if (!--sz)
				return true;
		}
		if (strcmp(p, "9223372036854775808") == 0) {
			val = _I64_MIN;
			return true;
		}
	}
	for (; sz; --sz, ++p) {
		if (*p >= '0' && *p <= '9') {
			if (val > _I64_MAX / 10)
				return false;
			val *= 10;
			int n = *p - '0';
			if (val > _I64_MAX - n)
				return false;
			val += n;
		}
		else
			return false;
	}
	if (neg)
		val = -val;
	return true;
}

bool Str2Int(const wstr_t &src, int &val)
{
	str_t s;
	return Str2Int(W2Str(src, s), val);
}

bool Str2Int(const wstr_t &src, __int64 &val)
{
	str_t s;
	return Str2Int(W2Str(src, s), val);
}

bool HexStr2UInt(const str_t &src, uint &val)
{
	val = 0;
	size_t sz = src.size();
	if (!sz)
		return false;
	const char *p = src.c_str();
	if (sz > 1 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
		p += 2;
		sz -= 2;
	}
	uint n;
	for (; sz; --sz, ++p) {
		if (*p >= '0' && *p <= '9')
			n = *p - '0';
		else if (*p >= 'A' && *p <= 'F' || *p >= 'a' && *p <= 'f') {
			if (*p >= 'a')
				n = *p - 'a';
			else
				n = *p - 'A';
			n += 10;
		}
		else
			return false;
		if (val > UINT_MAX / 16)
			return false;
		val *= 16;
		if (val > UINT_MAX - n)
			return false;
		val += n;
	}
	return true;
}

bool HexStr2UInt(const wstr_t &src, uint &val)
{
	str_t s;
	W2Str(src, s);
	return HexStr2UInt(s, val);
}

bool HexStr2UInt(const str_t &src, uint64 &val)
{
	val = 0;
	size_t sz = src.size();
	if (!sz)
		return false;
	const char *p = src.c_str();
	if (sz > 1 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
		p += 2;
		sz -= 2;
	}
	uint n;
	for (; sz; --sz, ++p) {
		if (*p >= '0' && *p <= '9')
			n = *p - '0';
		else if (*p >= 'A' && *p <= 'F' || *p >= 'a' && *p <= 'f') {
			if (*p >= 'a')
				n = *p - 'a';
			else
				n = *p - 'A';
			n += 10;
		}
		else
			return false;
		if (val > _UI64_MAX / 16)
			return false;
		val *= 16;
		if (val > _UI64_MAX - n)
			return false;
		val += n;
	}
	return true;
}

bool HexStr2UInt(const wstr_t &src, uint64 &val)
{
	str_t s;
	W2Str(src, s);
	return HexStr2UInt(s, val);
}

// {
template<class TYPE> inline static TYPE& TTrimRight(TYPE &s)
{
	TYPE::iterator first = s.begin(), last = s.end();
	while (last > first && *(last - 1) <= ' ')
		--last;
	s.erase(last, s.end());
	return s;
}

#define TRIM_RIGHT(TYPE) TYPE& TrimRight(TYPE &s) \
{ \
	return TTrimRight<TYPE>(s); \
}

TRIM_RIGHT(str_t)
TRIM_RIGHT(wstr_t)
// }

// {
template<class TYPE> inline static TYPE& TTrimLeft(TYPE &s)
{
	if (!s.empty()) {
		TYPE::iterator first = s.begin(), last = s.end();
		while (*first <= ' ' && ++first < last) ;
		s.erase(s.begin(), first);
	}
	return s;
}

#define TRIM_LEFT(TYPE) TYPE& TrimLeft(TYPE &s) \
{ \
	return TTrimLeft<TYPE>(s); \
}

TRIM_LEFT(str_t)
TRIM_LEFT(wstr_t)
// }

str_t& Trim(str_t &s)
{
	return TrimLeft(TrimRight(s));
}

wstr_t& Trim(wstr_t &s)
{
	return TrimLeft(TrimRight(s));
}

str_t& PadRight(str_t &s, size_t len, char c)
{
	if (s.length() < len)
		s.resize(len, c);
	return s;
}

wstr_t& PadRight(wstr_t &s, size_t len, wchar_t c)
{
	if (s.length() < len)
		s.resize(len, c);
	return s;
}

// {
template<class TYPE> inline static TYPE& TReplaceChar(TYPE &s,
	typename TYPE::value_type from, typename TYPE::value_type to)
{
	size_t n = 0;
	while ((n = s.find(from, n)) != TYPE::npos)
		s[n++] = to;
	return s;
}

#define REPLACE_CHAR(TYPE) TYPE& ReplaceChar(TYPE &s, TYPE::value_type from, TYPE::value_type to) \
{ \
	return TReplaceChar<TYPE>(s, from, to); \
}

REPLACE_CHAR(str_t)
REPLACE_CHAR(wstr_t)
// }

// {
template<class TYPE> inline static TYPE TReplaceStr(const TYPE &s,
	const TYPE &from, const TYPE &to)
{
	TYPE res;
	size_t n, m = 0;
	while ((n = s.find(from, m)) != TYPE::npos) {
		res += s.substr(m, n - m);
		res += to;
		m = n + from.size();
	}
	res += s.substr(m);
	return res;
}

#define REPLACE_STR(TYPE) TYPE ReplaceStr(const TYPE &s, const TYPE &from, const TYPE &to) \
{ \
	return TReplaceStr<TYPE>(s, from, to); \
}

REPLACE_STR(str_t)
REPLACE_STR(wstr_t)
// }

str_t& Data2StrHex(unsigned char *data, unsigned int dataSize, str_t &s)
{
	const char *hex = "0123456789abcdef";
	s.resize(dataSize * 2);
	unsigned int i, j;
	for (i = 0, j = 0; i < dataSize; ++i) {
		s[j++] = hex[data[i] >> 4];
		s[j++] = hex[data[i] & 0xf];
	}
	return s;
}

str_t Data2StrHex(unsigned char *data, unsigned int dataSize)
{
	str_t s;
	return Data2StrHex(data, dataSize, s);
}

// *** CommandLineParser ***

CommandLineParser::CommandLineParser(bool caseSensitive, const wchar_t *prefixes,
	const wchar_t *paramDelims, const wchar_t *valueDelims)
	:
	Prefixes(prefixes), ParamDelims(paramDelims), ValueDelims(valueDelims)
{
	m_cmp = caseSensitive ? ::wcscmp : _wcsicmp;
}

wstr_t& CommandLineParser::ParamStr(uint i, wstr_t &value)
{
	if (i < m_param.size())
		value = m_param[i];
	else
		value.erase();
	return value;
}

const wchar_t* CommandLineParser::ParamStr(uint i)
{
	if (i < m_param.size())
		return m_param[i].c_str();
	else
		return 0;
}

wstr_t& CommandLineParser::SimpleParamStr(uint i, wstr_t &value)
{
	if (i < m_simple.size())
		value = m_simple[i];
	else
		value.erase();
	return value;
}

const wchar_t* CommandLineParser::SimpleParamStr(uint i)
{
	if (i < m_simple.size())
		return m_simple[i].c_str();
	else
		return 0;
}

wstr_t& CommandLineParser::OptionStr(uint i, wstr_t &value)
{
	if (i < m_option_key_index.size())
		value = m_param[m_option_key_index[i]];
	else
		value.erase();
	return value;
}

const wchar_t* CommandLineParser::OptionStr(uint i)
{
	if (i < m_option_key_index.size())
		return m_param[m_option_key_index[i]].c_str();
	else
		return 0;
}

wstr_t& CommandLineParser::OptionKey(uint i, wstr_t &value)
{
	if (i < m_option_key.size())
		value = m_option_key[i].c_str();
	else
		value.erase();
	return value;
}

const wchar_t* CommandLineParser::OptionKey(uint i)
{
	if (i < m_option_key.size())
		return m_option_key[i].c_str();
	else
		return 0;
}

int CommandLineParser::FindOption(const wchar_t *optionName)
{
	int sz = (int)m_option_key.size();
	for (int i = 0; i < sz; ++i)
		if (m_cmp(m_option_key[i].c_str(), optionName) == 0)
			return i;
	return -1;
}

bool CommandLineParser::RemoveOption(const wchar_t *optionName)
{
	int i = FindOption(optionName);
	if (i >= 0) {
		m_option_key.erase(m_option_key.begin() + i);
		m_option_key_index.erase(m_option_key_index.begin() + i);
		m_option_value.erase(m_option_value.begin() + i);
		return true;
	}
	return false;
}

wstr_t& CommandLineParser::OptionValue(const wchar_t *optionName, wstr_t &value, bool remove)
{
	int i = FindOption(optionName);
	if (i >= 0) {
		value = m_option_value[i];
		if (remove) {
			m_option_key.erase(m_option_key.begin() + i);
			m_option_key_index.erase(m_option_key_index.begin() + i);
			m_option_value.erase(m_option_value.begin() + i);
		}
	}
	else
		value.erase();
	return value;
}

bool CommandLineParser::OptionValue(const wchar_t *optionName, int &value, bool remove)
{
	value = 0;
	wstr_t ws;
	if (OptionValue(optionName, ws, remove).empty())
		return false;
	return Str2Int(ws, value);
}

void CommandLineParser::ProcessParam(wstr_t &w, int &paramIndex, int fromIndex)
{
	if (w.empty())
		return;
	Trim(w);
	if (w.empty())
		return;
	if (++paramIndex > fromIndex) {
		assert(m_param.size() < INT_MAX);
		int i = (int)m_param.size();
		m_param.push_back(w);
		if (Prefixes.empty() || Prefixes.find(w[0]) != wstr_t::npos) {
			if (!Prefixes.empty()) {
				w.erase(0, 1);
				TrimLeft(w);
			}
			size_t j = w.find_first_of(ValueDelims);
			if (j == wstr_t::npos) {
				if (!w.empty()) {
					m_option_key.push_back(w);
					m_option_key_index.push_back(i);
					m_option_value.push_back(L"");
				}
			}
			else {
				wstr_t s(w, 0, j);
				TrimRight(s);
				w.erase(0, j + 1);
				Trim(w);
				int k = FindOption(s.c_str());
				if (k < 0) {
					m_option_key.push_back(s);
					m_option_key_index.push_back(i);
					m_option_value.push_back(w);
				}
				else {
					m_option_key_index[k] = i;
					m_option_value[k] = w;
				}
			}
}
		else
			m_simple.push_back(w);
	}
	w.erase();
}

void CommandLineParser::Parse(const wchar_t *commandLine, uint fromIndex,
	bool quotesDoubling, bool quotesAreDelimiters)
{
	m_param.resize(0);
	m_simple.resize(0);
	m_option_key.resize(0);
	m_option_value.resize(0);
	m_option_key_index.resize(0);
	wstr_t w;
	bool quote = false;
	int paramIndex = 0;
	const wchar_t *p = commandLine, *limit = commandLine + wcslen(commandLine);
	while (p < limit) {
		if (*p == '"') {
			if (quote) {
				if (quotesDoubling && p + 1 < limit && p[1] == '"') {
					w += '"';
					p += 2;
					continue;
				}
				if (quotesAreDelimiters)
					ProcessParam(w, paramIndex, fromIndex);
			}
			quote = !quote;
		}
		else if (quote || ParamDelims.find(*p) == wstr_t::npos)
			w += *p;
		else { // this is a delimiter
			if (!w.empty())
				ProcessParam(w, paramIndex, fromIndex);
			if (Prefixes.find(*p) != wstr_t::npos) // prefixes can intersect with delimiters
				w = *p;
		}
		++p;
	}
	ProcessParam(w, paramIndex, fromIndex);
}

// * Ini *

void Ini::Upcase(wstr_t &w)
{
	AsciiUpper(w);
}

void Ini::SetDefaultSection(const wchar_t *sectionName)
{
	Normalize(m_def_sec = sectionName);
}

bool Ini::GetBool(const wchar_t *sectionName, const wchar_t *keyName, bool &value)
{
	value = false;
	wstr_t w;
	if (GetStr(sectionName, keyName, w)) {
		AsciiUpper(w);
		if (w.compare(L"0") == 0 || w.compare(L"FALSE") == 0 || w.compare(L"NO") == 0 || w.compare(L"N") == 0)
			return true;
		if (w.compare(L"1") == 0 || w.compare(L"TRUE") == 0 || w.compare(L"YES") == 0 || w.compare(L"Y") == 0) {
			value = true;
			return true;
		}
	}
	return false;
}

bool Ini::GetInt(const wchar_t *sectionName, const wchar_t *keyName, int &value)
{
	wstr_t w;
	return GetStr(sectionName, keyName, w) && Str2Int(w, value = 0);
}

// * MemIni *

void MemIni::Clear()
{
	for (sec_map_vec_iter_t it = m_sec_map_vec.begin(); it != m_sec_map_vec.end(); ++it)
		delete *it;
	m_sec_map_vec.clear();
}

MemIni::~MemIni()
{
	Clear();
}

bool MemIni::AddLine(const wchar_t *line, const wchar_t *line_end, wstr_t &section)
{
	const wchar_t *start = line, *value_start = 0;
	if (*line == '[') {
		const wchar_t *p = std::find(++line, line_end, ']');
		if (p == line_end)
			return false;
		section.assign(line, p - line);
		Trim(section);
		return true;
	}
	wchar_t quote = 0;
	wstr_t key, value;
	do {
		if (!quote) {
			if (*line == ';') // the rest of the line is a comment
				break;
			if (!value_start)
				if (*line == '=') {
					const wchar_t *p = line;
					while (p > start && *(p - 1) <= ' ')
						--p;
					key.assign(start, p - start);
					value_start = line + 1;
					continue;
				}
			switch (*line) {
				case '\'':
				case '"':
					quote = *line;
					break;
			}
		}
		else
			if (*line == quote)
				quote = 0;
	} while (++line < line_end);
	if (key.empty())
		return *start == ';';
	bool ret = true;
	if (value_start && value_start < line) {
		value.assign(value_start, line - value_start);
		Trim(value);
		size_t len = value.size();
		if (len >= 2 && (value[0] == '\'' || value[0] == '"'))
			if (value[0] == value[len - 1]) {
				value.erase(len - 1);
				value.erase(0, 1);
			}
			else
				ret = false;
	}
	else
		ret = false;
	key_value_map_t *map;
	Normalize(section);
	sec_iter_t it = std::find(m_sections.begin(), m_sections.end(), section);
	if (it == m_sections.end()) {
		m_sections.push_back(section);
		map = new key_value_map_t;
		m_sec_map_vec.push_back(map);
	}
	else
		map = m_sec_map_vec[it - m_sections.begin()];
	Normalize(key);
	(*map)[key] = value;
	return ret;
}

bool MemIni::Parse(const void *mem, uint byteSize)
{
	bool ret = true;
	Clear();
	const wchar_t *cur = (const wchar_t*)mem, *last = cur + byteSize / sizeof(wchar_t), *line = 0;
	wstr_t section;
	while (cur < last) {
		if (line)
			switch (*cur) {
				case 0:
				case '\r':
				case '\n':
					if (!AddLine(line, cur, section))
						ret = false;
					line = 0;
					break;
			}
		else
			if (*cur > ' ')
				line = cur;
		++cur;
	}
	if (line)
		if (!AddLine(line, last, section))
			ret = false;
	return ret;
}

bool MemIni::FGetStr(const wchar_t *sectionName, const wchar_t *keyName, wstr_t &value)
{
	wstr_t w;
	sec_iter_t it = std::find(m_sections.begin(), m_sections.end(), Normalize(w = sectionName));
	if (it == m_sections.end())
		return false;
	key_value_map_t *key_map = m_sec_map_vec[it - m_sections.begin()];
	key_value_map_t::const_iterator it1 = key_map->find(Normalize(w = keyName));
	if (it1 == key_map->end())
		return false;
	value = it1->second;
	return true;
}

} // namespace
